/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "generators/bridgeGenerator.h"
#include "factory/factories.h"

#include <boost/any.hpp>

namespace NAMESPACE{

// ****** BridgeGenerator implementation **********************************

//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

BridgeGenerator::~BridgeGenerator(){
    free_buffer();
}

BridgeGenerator::BridgeGenerator() : AbstractGenerator(0),
                                     from_generator(0),
                                     grab_instant(0){}

BridgeGenerator::BridgeGenerator(const BridgeGenerator& bg) : AbstractGenerator(bg),
                                                              from_generator(bg.from_generator),
                                                              grab_instant(bg.grab_instant){}

BridgeGenerator& BridgeGenerator::operator=(const BridgeGenerator& bg){
    if(this == &bg) return *this;

    AbstractGenerator::operator =(bg);

    from_generator = bg.from_generator;
    grab_instant = bg.grab_instant;

    return *this;
}


//  _____________________________________________________
// |   Other constructors                                |
// |_____________________________________________________|

BridgeGenerator::BridgeGenerator(const Generator& from_gen, 
                                 const IOKey& k,
                                 const size_t& _grab_instant) : AbstractGenerator(k, 0),
                                                                from_generator(&from_gen),
                                                                grab_instant(_grab_instant){
   key->setType(from_generator->get_key().getType());
}

// ......................................................

//  _____________________________________________________
// |   Setters                                           |
// |_____________________________________________________|
Generator& BridgeGenerator::set_key(const std::string& ns, const std::string& key_name) throw(bad_generator) {
    if(!from_generator) throw bad_generator("To determine the type of the key you have to specify the source generator");

    AbstractGenerator::set_key(ns, key_name);
    key->setType(from_generator->get_key().getType());
    return *this;
}

BridgeGenerator& BridgeGenerator::set_from_generator(const Generator& from_gen){
    from_generator = &from_gen;
    return *this;
}

BridgeGenerator& BridgeGenerator::set_grab_instant(const size_t& _grab_instant){
    grab_instant = _grab_instant;
    return *this;
}
// ........................................................

//  _____________________________________________________
// |   Getters                                           |
// |_____________________________________________________|
// .......................................................

//  _____________________________________________________
// |   Private functions                                 |
// |_____________________________________________________|

memory_size_t BridgeGenerator::measure_memory(){ return generated_buffer->size();}
void BridgeGenerator::free_element(IOElement* el){}
// .......................................................

IOElement* BridgeGenerator::generate(const IOKey& key) throw(bad_generator){
    if(!from_generator) throw bad_generator("No source generator specified");

    const AbstractGenerator* g = dynamic_cast<const AbstractGenerator*>(from_generator);

    IOElement* tmp = *(g->get_buffer().rbegin() + grab_instant);
    if(g->get_buffer().size() - 1 < grab_instant) throw bad_generator("The given source generator is not valid: not enough history");

    HandledIOElement* tmp_handled = dynamic_cast<HandledIOElement*>(tmp);
    if(!tmp_handled) throw bad_generator("The given source generator is not valid: helded IOElement not of subclass HandledIOElement");

    IOElement* el = new HandledIOElement(*tmp_handled);
    el->setIOKey(key);

    return el;
}

bool BridgeGenerator::operator==(const Generator& g) const{
    const BridgeGenerator* bg = dynamic_cast<const BridgeGenerator*>(&g);
    if(!bg) return false;

    if(AbstractGenerator::operator!=(g)) return false;

    return from_generator == bg->from_generator &&
           grab_instant   == bg->grab_instant;
}

bool BridgeGenerator::operator!=(const Generator& ag) const{
    return !BridgeGenerator::operator==(ag);
}

// ****** BridgeGeneratorFactory implementation ********************************
REGISTER_PARSE_TYPE(BridgeGenerator);
class BridgeGeneratorFactory : public ConfigurableFactory<Generator>{
public:
    BridgeGeneratorFactory() : ConfigurableFactory<Generator>(){
        FactoriesPool<Generator>::get_instance()->add_factory(TypeParseTraits<BridgeGenerator>::name(), this);
        IOKey k0, k1, k2;
        //k0.set_key(PARAMETER_NS, "pointer.generator");  k0.setType(TypeParseTraits<Generator*>::name());
        k0.set_key(PARAMETER_NS, "string.id");          k0.setType(TypeParseTraits<std::string>::name());
        k1.set_key(PARAMETER_NS, "iokey");              k1.setType(TypeParseTraits<IOKey>::name());
        k2.set_key(PARAMETER_NS, "time.difference");    k2.setType(TypeParseTraits<int>::name());
        cfg_tmplt.add(k0).add(k1).add(k2);
    }

    Generator* build() const{
        return new BridgeGenerator();
    }

    Generator* build(const Configuration& cfg) const throw(bad_parameter){
        Generator* from_g = cfg.get_parameter<Generator*>(cfg_tmplt.param_at(0));
        IOKey k           = cfg.get_parameter<IOKey>(cfg_tmplt.param_at(1));
        int grab_instant  = cfg.get_parameter<int>(cfg_tmplt.param_at(2));
        return new BridgeGenerator(*from_g, k, grab_instant);
    }
}instance_of_bridge_generator_factory;

// -----------------------------------------------------------------------------

}
